import math
from array import array


# 可散列
class Vector2d:
    __slots__ = ['__x', '__y']

    # typecode 是类属性，在 Vector2d 实例和字节序列之间转换时使用
    typecode = 'd'

    def __init__(self, x, y):
        # 换成浮点数，尽早捕获错误
        self.__x = float(x)
        self.__y = float(y)

    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y

    def __iter__(self):
        # 变成可迭代的对象，这样才能拆包
        return (i for i in (self.x, self.y))

    def __repr__(self):
        class_name = type(self).__name__
        # !r 返回带引号的表示形式
        # 因为 Vector2d 实例是可迭代的对象，所以 *self 会把x 和 y 分量提供给 format 函数
        return '{}({!r},{!r})'.format(class_name, *self)

    def __str__(self):
        # 从可迭代的 Vector2d 实例中可以轻松地得到一个元组，显示为一个有序对
        return str(tuple(self))

    def __bytes__(self):
        return bytes([ord(self.typecode)]) + bytes(array(self.typecode, self))

    def __hash__(self):
        return hash(self.x) ^ hash(self.y)

    def __eq__(self, other):
        # 缺陷  Vector(3, 4) == [3, 4]
        return tuple(self) == tuple(other)

    def __abs__(self):
        return math.hypot(self.x, self.y)

    def __bool__(self):
        return bool(abs(self))

    def __format__(self, fmt_spec=''):
        if fmt_spec.endswith('p'):
            fmt_spec = fmt_spec[:-1]
            coords = (abs(self), self.angle())
            outer_fmt = '<{}, {}>'
        else:
            coords = self
            outer_fmt = '({}, {})'
        components = (format(c, fmt_spec) for c in coords)
        return outer_fmt.format(*components)

    def angle(self):
        # 计算角度
        return math.atan2(self.x, self.y)

    @classmethod
    def frombytes(cls, octets):
        # cls - 类本身
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        return cls(*memv)


if __name__ == '__main__':
    v1 = Vector2d(3, 4)
    print(v1.x, v1.y)
    x, y = v1
    print((x, y))
    print(repr(v1))
    v1_clone = eval(repr(v1))
    print(v1 == v1_clone)
    print(v1)
    octets = bytes(v1)
    print(octets)
    print(abs(v1))
    print(bool(v1), bool(Vector2d(0, 0)))
    print(Vector2d.frombytes(octets))
    print(format(v1))
    print(format(v1, '.3f'))
    print(format(v1, '.3e'))
    print(format(Vector2d(1, 1), 'p'))
    print(format(Vector2d(1, 1), '.3ep'))
    print(format(Vector2d(1, 1), '.5fp'))
    # 可散列
    v1 = Vector2d(3, 4)
    v2 = Vector2d(3.1, 4.2)
    print(hash(v1), hash(v2))
    s = set([v1, v2])
    print(v1.__dict__)
    print(v1._Vector2d__x)
